/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2008 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

using System;
using System.Web;
using System.Collections.Generic;

using Gtk;
using Glade;

using Anculus.Core;
using Anculus.Gui;

using Galaxium.Core;
using Galaxium.Protocol;
using Galaxium.Protocol.Gui;
using Galaxium.Gui;

namespace Galaxium.Gui.GtkGui
{
	public abstract class GroupChatWidget : AbstractChatWidget, ISessionListener
	{
		const int _snapSize = 5;
		
		protected static Dictionary<ISession, EventHandler> _fontChangedHandlers = new Dictionary<ISession, EventHandler> ();
		
		protected Label _status_label = null;
		protected Image _activityImage = null;
		protected Toolbar _entryToolbar = null;
		protected Label _personalLabel = null;
		protected Image _personalImage = null;
		protected ScrolledWindow _messageEntryWindow = null;
		protected MessageDisplayWidget _message_display;
		protected MessageEntryWidget _messageEntry;
		
		protected bool _historyComplete = false;
		protected List<IMessage> _incomingMessages = new List<IMessage> ();
		
		public MessageEntryWidget MessageEntry { get { return _messageEntry; } }
		public MessageDisplayWidget MessageDisplay { get { return _message_display; } }
		
		// Common properties that we can include.
		public override bool ShowActionToolbar
		{
			get { return Conversation.PrimaryContact.ShowActionToolbar; }
			set { Conversation.PrimaryContact.ShowActionToolbar = value; if (value) _window.Toolbar.ShowAll(); else _window.Toolbar.Visible = false; }
		}
		
		public override bool ShowInputToolbar
		{
			get { return Conversation.PrimaryContact.ShowInputToolbar; }
			set { Conversation.PrimaryContact.ShowInputToolbar = value; if (value) _entryToolbar.ShowAll(); else _entryToolbar.Visible = false; }
		}
		
		public override bool ShowPersonalMessage
		{
			get { return Conversation.PrimaryContact.ShowPersonalMessage; }
			set { Conversation.PrimaryContact.ShowPersonalMessage = value; if (value) _identificationSpacer.ShowAll(); else _identificationSpacer.Visible = false; }
		}
		
		public override bool ShowAccountImage
		{
			get { return Conversation.PrimaryContact.ShowAccountImage; }
			set { Conversation.PrimaryContact.ShowAccountImage = value; if (value) _leftInputBox.ShowAll(); else _leftInputBox.Visible = false; }
		}
		
		public override bool ShowContactImage
		{
			get { return Conversation.PrimaryContact.ShowContactImage; }
			set { Conversation.PrimaryContact.ShowContactImage = value; if (value) _rightInputBox.ShowAll(); else _rightInputBox.Visible = false; }
		}
		
		public override bool ShowTimestamps
		{
			get { return Conversation.PrimaryContact.ShowTimestamps; }
			set { Conversation.PrimaryContact.ShowTimestamps = value; }
		}
		
		public override bool EnableSounds
		{
			get { return Conversation.PrimaryContact.EnableSounds; }
			set { Conversation.PrimaryContact.EnableSounds = value; }
		}
		
		public override bool UseDefaultView
		{
			get { return Conversation.PrimaryContact.UseDefaultView; }
			set { Conversation.PrimaryContact.UseDefaultView = value; }
		}
		
		public GroupChatWidget (IContainerWindow<Widget> window, IConversation conversation) : base (window, conversation)
		{
			// Register this as a session listener
			SessionUtility.AddSessionListener (this);
			
			// Register and setup the font changed handler.
			if (!_fontChangedHandlers.ContainsKey (_conversation.Session))
				_fontChangedHandlers.Add (_conversation.Session, null);
			
			_fontChangedHandlers[_conversation.Session] += delegate { LoadFont (); };
			
			_message_display = new MessageDisplayWidget (_conversation);
			
			_messageEntry = new MessageEntryWidget (_conversation);
			_messageEntry.DragReceived += OnDragReceived;
			
			_messageEntryWindow = new ScrolledWindow ();
			_messageEntryWindow.ShadowType = ShadowType.None;
			_messageEntryWindow.HscrollbarPolicy = PolicyType.Never;
			_messageEntryWindow.VscrollbarPolicy = PolicyType.Automatic;
			_messageEntryWindow.Add (_messageEntry);
			_messageEntryWindow.ShowAll ();
			
			_personalLabel = new Gtk.Label ();
			_personalLabel.Xalign = 0.0f;
			_personalLabel.UseMarkup = true;
			_personalLabel.Ellipsize = Pango.EllipsizeMode.End;
			_personalLabel.SingleLineMode = false;
			_personalLabel.Wrap = true;
			
			_personalImage = new Image ();
			_personalImage.Pixbuf = IconUtility.GetIcon ("galaxium-status-online", IconSizes.Small);
			
			_entryToolbar = new Gtk.Toolbar ();
			_entryToolbar.ToolbarStyle = ToolbarStyle.BothHoriz;
			_entryToolbar.Orientation = Orientation.Horizontal;
			_entryToolbar.IconSize = IconSize.SmallToolbar;
			
			_activityImage = new Gtk.Image ();
			
			_status_label = new Label ();
			_status_label.UseMarkup = true;
			_status_label.Xalign = 0.0f;
		}
		
		public override void Initialize ()
		{
			// You must call this before anything else.
			base.Initialize ();
			
			_widgetBox.BorderWidth = 3;
			
			HBox personalBox = new HBox ();
			personalBox.PackStart (_personalImage, false, false, 0);
			personalBox.PackStart (_personalLabel, true, true, 0);
			personalBox.Spacing = 5;
			personalBox.BorderWidth = 1;
			
			SetChatWidget (ChatWidgetPositions.LeftDisplayPane, _message_display);
			SetChatWidget (ChatWidgetPositions.MessageEntry, _messageEntryWindow);
			SetChatWidget (ChatWidgetPositions.EntryToolbar, _entryToolbar, false, false, 0);
			SetChatWidget (ChatWidgetPositions.Identification, personalBox, false, true, 0);
			SetChatWidget (ChatWidgetPositions.LeftStatus, _activityImage, false, true, 0);
			SetChatWidget (ChatWidgetPositions.CenterStatus, _status_label);
			
			// Because we have to remove the widget before destroying the window.
			_nativeWidget.Unrealized += delegate
			{
				RemoveChatWidget (ChatWidgetPositions.LeftDisplayPane);
			};
			
			// Initialize must be called in order for the user interface to work.
			_messageEntry.KeyPressEvent += KeyPressEvent;
			_messageEntry.KeyReleaseEvent += KeyReleaseEvent;
			
			_activityImage.Pixbuf = IconUtility.GetIcon("galaxium-typing_blank");
			
			_vertical_pane.Position = 600;
			
			// We aren't ready until the GdkWindows for the widgets are created
			// otherwise WinkDisplay hits a null reference trying to find it's
			// position and size
			_nativeWidget.ExposeEvent += BecomeReady;
			
			LoadFont ();
			
			_messageEntry.GrabFocus ();
		}
		
		public override void Destroy ()
		{
			_conversation.Ready = false;
		}
		
		protected virtual void BecomeReady (object sender, EventArgs args)
		{
			ThrowUtility.ThrowIfNull ("message_display", _message_display);
			
			_nativeWidget.ExposeEvent -= BecomeReady;
			_conversation.Ready = true;
			
			/*Log.Debug ("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14}",
			           _inputBox.HeightRequest, _leftInputBox.HeightRequest, _rightInputBox.HeightRequest,
			           _mainFrame.HeightRequest, _entryBox.HeightRequest, _entryToolbarBox.HeightRequest, _messageEntryBox.HeightRequest,
			           _ownImage.HeightRequest, _contactImage.HeightRequest, _messageEntry.HeightRequest, _entryToolbar.HeightRequest,
			           _ownImage.Children[0].HeightRequest, _contactImage.Children[0].HeightRequest,
			           (_ownImage.Children[0] as EventBox).Child.HeightRequest, (_contactImage.Children[0] as EventBox).Child.HeightRequest);*/
			
			
			/*if (Configuration.Logging.Section.GetBool (Configuration.Logging.EnableLogging.Name, Configuration.Logging.EnableLogging.Default))
			{
				if (EnableLogging)
					_message_display.AddEvent ("This conversation is being logged.");
				else
					_message_display.AddEvent ("This conversation is not being logged.");
			}
			else
				_message_display.AddEvent ("Logging is currently disabled in the preferences.");*/
		}
		
		public void ProcessLogs ()
		{
			var log = Conversation.ConversationLog;

			if (log != null)
			{
				try
				{
					foreach (var entry in log.GetHistoryEntries ())
					{
						var entity = (String.IsNullOrEmpty (entry.UniqueIdentifier) && String.IsNullOrEmpty (entry.DisplayName)) ? _conversation.Session.Account : (IEntity) GetConversationContact (entry.UniqueIdentifier, entry.DisplayName);
						
						IMessage msg = new Message ((entry.IsEvent ? MessageFlag.Event : MessageFlag.Message) | MessageFlag.History, entity, null, entry.TimeStamp);
						msg.SetMarkup (entry.Message, null);
						
						_message_display.AddMessage (msg);
					}
				}
				catch (Exception ex)
				{
					Anculus.Core.Log.Error (ex, "Error displaying unread history");
				}
				
				try
				{
					foreach (var entry in log.GetUnreadEntries ())
					{
						var entity = (String.IsNullOrEmpty (entry.UniqueIdentifier) && String.IsNullOrEmpty (entry.DisplayName)) ? _conversation.Session.Account : (IEntity) GetConversationContact (entry.UniqueIdentifier, entry.DisplayName);
						
						IMessage msg = new Message ((entry.IsEvent ? MessageFlag.Event : MessageFlag.Message), entity, null, entry.TimeStamp);
						msg.SetMarkup (entry.Message, null);
						
						_message_display.AddMessage (msg);
					}
				}
				catch (Exception ex)
				{
					Anculus.Core.Log.Error (ex, "Error displaying unread history");
				}
			}
			
			_historyComplete = true;
		}
		
		public void ProcessSessionMessage (ISessionMessage message)
		{
			ThrowUtility.ThrowIfNull ("message", message);
			ThrowUtility.ThrowIfNull ("message_display", _message_display);
			
			_message_display.AddEvent (message.Message);
		}
		
		public override void Focus ()
		{
			_messageEntry.GrabFocus();
		}
		
		public override void Update ()
		{
			var config = Configuration.Conversation.Section;
			_window.Toolbar.Visible = UseDefaultView ? config.GetBool (Configuration.Conversation.ShowActivityToolbar.Name, Configuration.Conversation.ShowActivityToolbar.Default) : ShowActionToolbar;
			_entryToolbar.Visible = UseDefaultView ? config.GetBool (Configuration.Conversation.ShowInputToolbar.Name, Configuration.Conversation.ShowInputToolbar.Default) : ShowInputToolbar;
			_identificationSpacer.Visible = UseDefaultView ? config.GetBool (Configuration.Conversation.ShowIdentification.Name, Configuration.Conversation.ShowIdentification.Default) : ShowPersonalMessage;
		}
		
		public abstract void LoadFont ();
		public abstract void SaveFont ();
		public abstract void SendFile (string filename);
		
		[GLib.ConnectBefore	()]
		protected virtual void KeyReleaseEvent (object sender, KeyReleaseEventArgs args)
		{
			
		}
		
		[GLib.ConnectBefore ()]
		protected virtual void KeyPressEvent (object sender, KeyPressEventArgs args)
		{
			
		}
		
		protected virtual void OwnImageButtonPressEvent (object sender, ButtonPressEventArgs args)
		{
			
		}
		
		private void OnDragReceived (object sender, DragReceivedEventArgs args)
		{
			if (args.UriList != null && args.UriList.Length > 0)
			{
				if (args.UriList.Length > 3)
				{
					string error = GettextCatalog.GetString ("You may only send a maximum of 3 files to an individual at any one time.");
					MessageDialog errordialog = new MessageDialog (null, DialogFlags.Modal, Gtk.MessageType.Error, ButtonsType.Ok, error);
					errordialog.Run ();
					errordialog.Destroy ();
				}
				
				for (int i = 0; i < ((args.UriList.Length < 3) ? args.UriList.Length : 3); i++)
					SendFile (HttpUtility.UrlDecode(args.UriList[i].Substring(7)));
			}
		}
		
		protected virtual void GeneratePersonalLabel ()
		{
			string label = string.Empty;
			
			if (_conversation.PrimaryContact.DisplayMessage != null && _conversation.PrimaryContact.DisplayMessage != String.Empty)
				label += "<span size=\"small\"><i>"+XmlUtility.Encode (_conversation.PrimaryContact.DisplayMessage)+"</i></span>";
			else
				label += "<span size=\"small\"><i></i>No personal message</span>";
			
			_personalLabel.Markup = label;
			_personalLabel.Ellipsize = Pango.EllipsizeMode.End;
			_personalLabel.SingleLineMode = false;
			_personalLabel.Wrap = true;
		}
	}
}
